﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using TrackingSim.GraphicSystem.Models;

namespace TrackingSim.GameSystem
{
    public class Player : GameObject
    {
        private static int player_num_counter = 0;

        private string m_name;
        private int m_payer_num;

        private Vector3 m_up;
        private Vector3 m_forward;
        private Vector3 m_left;

        private Vector3 m_position;
        private Vector3 m_velocity;
        private float m_speed;
        private float m_accel;
        private Vector3 m_acceleration;
        private Matrix m_worldMatrix;

        private ObjectModel m_object_model;

        private float rot_deg;

        private bool m_moveEnabled;

        public Player()
        {
            m_payer_num = player_num_counter;
            ++player_num_counter;

            m_up = new Vector3();
            m_forward = new Vector3();
            m_left = new Vector3();
            m_position = new Vector3();
            m_velocity = new Vector3();
            m_speed = 0.0f;
            m_accel = 0.0f;
            m_acceleration = new Vector3();
            m_worldMatrix = Matrix.Identity;

            rot_deg = 3.0f;
            m_moveEnabled = false;
        }

        public Player( Vector3 position, Vector3 forward, Vector3 up)
        {
            m_payer_num = player_num_counter;
            ++player_num_counter;

            m_position = position;
            m_forward = forward;
            m_up = up;
            m_left = Vector3.Cross(m_up, m_forward);
            m_velocity = new Vector3();
            m_speed = 0.0f;
            m_accel = 0.0f;
            m_acceleration = new Vector3();
            m_worldMatrix = Matrix.Identity;

            rot_deg = 3.0f;
        }

        public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }

        public int PlayerNumber
        {
            get { return m_payer_num; }
        }

        public ObjectModel Model
        {
            get { return m_object_model; }
            set { m_object_model = value; }
        }

        public Vector3 Up
        {
            get { return m_up; }
            set { m_up = value; }
        }

        public Vector3 Left
        {
            get { return m_left; }
            set { m_left = value; }
        }

        public Vector3 Forward
        {
            get { return m_forward; }
            set { m_forward = value; }
        }

        public Vector3 Pos
        {
            get { return m_position; }
            set { m_position = value; }
        }

        public Vector3 Vel
        {
            get { return m_velocity; }
            set { m_velocity = value; }
        }

        public float Speed
        {
            get { return m_speed; }
            set { m_speed = value; }
        }

        public Vector3 Accel
        {
            get { return m_acceleration; }
            set { m_acceleration = value; }
        }

        public Matrix WorldMatrix
        {
            get 
            {
                Matrix.CreateWorld(ref m_position, ref m_forward, ref m_up, out m_worldMatrix);
                return m_worldMatrix; 
            }
            //set { m_worldMatrix = value; }
        }

        public bool MoveEnabled
        {
            get { return m_moveEnabled; }
            set { m_moveEnabled = value; }
        }

        public void resetPos()
        {
            Pos = new Vector3(0.0f, 0.0f, 0.0f);
        }

        public void moveForward()
        {
            Pos = Pos + Forward;
        }

        public void moveBackward()
        {
            Pos = Pos - Forward;
        }

        public void turnLeft()
        {
            rotate_yaw(MathHelper.ToRadians(rot_deg));
        }

        public void turnRight()
        {
            rotate_yaw(-MathHelper.ToRadians(rot_deg));
        }

        public void rollLeft()
        {
            rotate_roll(MathHelper.ToRadians(rot_deg));
        }

        public void rollRight()
        {
            rotate_roll(-MathHelper.ToRadians(rot_deg));
        }

        public void pitchUp()
        {
            rotate_pitch(MathHelper.ToRadians(rot_deg));
        }

        public void pitchDown()
        {
            rotate_pitch(-MathHelper.ToRadians(rot_deg));
        }

        private void rotate_yaw(float theta)
        {
            Matrix rot = Matrix.CreateFromAxisAngle(m_up, theta);

            m_forward = Vector3.Transform(m_forward, rot );
            m_forward.Normalize();
            m_left = Vector3.Cross(m_up, m_forward);
            m_left.Normalize();
            m_velocity = m_speed * Vector3.Normalize(m_forward);
            m_acceleration = m_accel * Vector3.Normalize(m_velocity);
        }

        private void rotate_pitch(float theta)
        {
            Matrix rot = Matrix.CreateFromAxisAngle(m_left, theta);

            m_forward = Vector3.Transform(m_forward, rot);
            m_forward.Normalize();
            m_up = Vector3.Cross(m_forward, m_left);
            m_up.Normalize();
            m_velocity = m_speed * Vector3.Normalize(m_forward);
            m_acceleration = m_accel * Vector3.Normalize(m_velocity);
        }

        private void rotate_roll(float theta)
        {
            Matrix rot = Matrix.CreateFromAxisAngle(m_forward, theta);

            m_up = Vector3.Transform(m_up, rot);
            m_up.Normalize();
            m_left = Vector3.Cross(m_up, m_forward);
            m_left.Normalize();
            m_velocity = m_speed * Vector3.Normalize(m_forward);
            m_acceleration = m_accel * Vector3.Normalize(m_velocity);
        }

        public void move(double dt)
        {
            if( m_moveEnabled )
            {
                float dt_f = (float)dt / 1000.0f;

                m_forward.Normalize();
                m_velocity.X = m_speed * m_forward.X;
                m_velocity.Y = m_speed * m_forward.Y;
                m_velocity.Z = m_speed * m_forward.Z;

                m_position.X += 0.5f * m_acceleration.X * dt_f * dt_f + m_velocity.X * dt_f;
                m_position.Y += 0.5f * m_acceleration.Y * dt_f * dt_f + m_velocity.Y * dt_f;
                m_position.Z += 0.5f * m_acceleration.Z * dt_f * dt_f + m_velocity.Z * dt_f;

                m_velocity.X += m_acceleration.X * dt_f;
                m_velocity.Y += m_acceleration.Y * dt_f;
                m_velocity.Z += m_acceleration.Z * dt_f;
            }
        }

        public override void receiveEvent(Events.GameEvent _event)
        {
            switch (_event.getType())
            {
                case Events.GameEventType.A:
                    {
                        Events.EventA a = _event as Events.EventA;
                        break;
                    }
                case Events.GameEventType.B:
                    {
                        Events.EventB b = _event as Events.EventB;
                        break;
                    }
                default:
                    {
                        break;
                    }
            }
        }
    }
}
